package com.ftar.urgent.voice;

import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ftar.urgent.model.AsrConfigModel;
import com.rokid.utils.StringUtils;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.java_websocket.util.Base64;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.Key;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

/**
 * @author: wei.jin
 * @date: 2023/7/4
 * @email: wei.jin@fanttec.com
 * @description：
 */
public class OnLineAsrManager {
    private String TAG = this.getClass().getSimpleName();
    private AsrSocket mAsrSocket;
    private OnAsrResultListener mOnAsrResultListener;
    private OnSocketStatusListener mOnSocketStatusListener;
    private FileOutputStream fos;

    private static OnLineAsrManager mOnLineAsrManager;

    private int mConnectStatus = 0;

    public interface OnAsrResultListener {
        void onVolume(int volume);
        void onResult(String result);
    }

    public interface OnSocketStatusListener {
        void onConnected();
        void onLost();
    }

    public static OnLineAsrManager getInstance() {
        return getInstance(null);
    }

    public static OnLineAsrManager getInstance(String path) {
        if(mOnLineAsrManager == null) {
            mOnLineAsrManager = new OnLineAsrManager(path);
        }
        return mOnLineAsrManager;
    }
    public OnLineAsrManager(String path) {
        if(StringUtils.isNullOrEmpty(path)){
            path = "/sdcard";
        }
        createRecordFile(path + "/test.pcm");
    }

    public String getToken(String key, String secretstr) throws IOException {
        Map<String, Object> header = new HashMap<>();
        header.put("alg", "HS256");
        header.put("typ", "JWT");
        long now = System.currentTimeMillis();

        String secret = Base64.encodeBytes(secretstr.getBytes());

        Key hs256 = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        Date exp = new Date(System.currentTimeMillis() + 30 * 60 * 1000);// Over time
        String token = Jwts.builder()
                .setHeaderParams(header)
                .setExpiration(exp)
                .setIssuer(key)
                .setIssuedAt(new Date(now))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
        try {
//            JWT jwt = new JWT(token);
            Jwts.parserBuilder().setSigningKey(secret).build().parseClaimsJws(token);
        } catch (ExpiredJwtException e) {
            e.printStackTrace();
            token = "";
        }

        return token;
    }


    public void trustAllHosts(WebSocketClient wsc) {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }

            public void checkClientTrusted(X509Certificate[] chain,
                                           String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain,
                                           String authType) throws CertificateException {
            }
        }};

        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            wsc.setSocketFactory(sc.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void startAsr(List<String> hotwords, OnAsrResultListener onAsrResultListener, OnSocketStatusListener onSocketStatusListener) {
        mOnAsrResultListener = onAsrResultListener;
        mOnSocketStatusListener = onSocketStatusListener;
        if(mAsrSocket == null) {
            URI uri = null;
            String token = "";
            try {
                token = getToken("mUN8ipMmAlyBj9M7BzKkGobO","apc0i4N5b0ovAF0BExnomAZIH12orU10");

                uri = new URI("wss://ai.cubigdata.cn:5001/openapi/speech/asr/8k");
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            HashMap<String, String> headers = new HashMap<String, String>();
            headers.put("Authorization", "Bearer "+token);
            AsrConfigModel asrConfigModel = new AsrConfigModel();
            AsrConfigModel.ConfigDTO configDTO = new AsrConfigModel.ConfigDTO();
            configDTO.setSpkNum(1);
            configDTO.setSampFreq(48000);
            configDTO.setAddPunc(0);
            configDTO.setEndpointMode(3);
            configDTO.setHotwordsWeight(3.0f);
            configDTO.setHotwords(hotwords);
            asrConfigModel.setConfig(configDTO);
            Object jsonObject = JSON.toJSON(asrConfigModel);
            mAsrSocket = new AsrSocket(uri, headers) {
                @Override
                public void onOpen(ServerHandshake handshakedata) {
                    super.onOpen(handshakedata);

                    Log.d(TAG, "opend:" + jsonObject.toString());
                    mAsrSocket.send(jsonObject.toString());
                }

                @Override
                public void onMessage(String message) {
                    super.onMessage(message);

                    Log.d(TAG, "onMessage:" + message);
                    JSONObject jo = JSONObject.parseObject(message);
                    JSONObject rj = jo.getJSONObject("result");

                    String text = rj.getString("text");
                    if (jo.getBooleanValue("is_endpoint")) {
                        if (mOnAsrResultListener != null) {
                            mOnAsrResultListener.onResult(text);
                        }
                    } else {
                    }
                }
            };

            trustAllHosts(mAsrSocket);
            mAsrSocket.connect();
        }

        if(mAsrSocket.isClosed()) {
            mAsrSocket.reconnect();
        }else {
        }
    }

    /**
     * Send raw audio
     * @param buffer
     */
    public void sendData(byte[] buffer, int volume) {
        writeDataTOFile(buffer);

        if(mAsrSocket != null) {
            if (mAsrSocket.isOpen()) {
                mAsrSocket.send(buffer);
                if (mOnSocketStatusListener != null && mConnectStatus == 0) {
                    mConnectStatus = 1;
                    mOnSocketStatusListener.onConnected();
                }
            }
            if (mAsrSocket.isClosed()) {
                if (mOnSocketStatusListener != null && mConnectStatus == 1) {
                    mConnectStatus = 0;
                    mOnSocketStatusListener.onLost();
                }
            }
        }

        if(mOnAsrResultListener != null) {
            mOnAsrResultListener.onVolume(volume);
        }
    }

    public void stopRecognize() {
        if(mAsrSocket != null) {
            mAsrSocket.close();
        }
    }

    public void createRecordFile(String filePath) {
        fos = null;
        try {
            File file = new File(filePath);
            if (file.exists()) {
//                file.delete();
            }
            fos = new FileOutputStream(file);// 建立一个可存取字节的文件
        } catch (IllegalStateException e) {
            Log.e("AudioRecorder", e.getMessage());
            throw new IllegalStateException(e.getMessage());
        } catch (FileNotFoundException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    }

    /**
     * write audio data to file
     *
     */
    private void writeDataTOFile(byte[] audioData) {

        if(fos != null) {
            try {
                fos.write(audioData);
            } catch (IOException e) {
                Log.e("AudioRecorder", e.getMessage());
            }
        }
    }
}
